/**
*  \file spi_hw.c
*
*  \brief spi driver function
*
*  \author dajin.li  <dajin.li@linde-china.cn>
*
*/

/*===============================================[ private includes  ]================================================*/
#include <xmc_gpio.h>
#include <xmc_spi.h>
#include <xmc4500/io.h>
#include <xmc4500/processor.h>
#include <xmc4500/types_local.h>
#include "../sched/sched.h"
#include "../pal/pal_mem.h"

/*===============================================[ private defines   ]================================================*/

#define TICKS_PER_SECOND                 1000
#define TICKS_WAIT                       1000

#define SPI_MISO                         P2_2
#define SPI_MOSI                         P2_5
#define SPI_CS                           P2_3
#define SPI_SCLK                         P2_4
#define SPI_CHANNEL                      XMC_SPI0_CH1
#define SPI_MISO_SOURCE                  USIC0_C1_DX0_P2_2

#define tlf_spi_delay_cnt                100 /*100*100us == 10ms*/


/*===============================================[ private variables ]================================================*/

XMC_SPI_CH_CONFIG_t spi_config =
{
  .baudrate = 2000000,
  .bus_mode = XMC_SPI_CH_BUS_MODE_MASTER,
  .selo_inversion = XMC_SPI_CH_SLAVE_SEL_INV_TO_MSLS,
  .parity_mode = XMC_USIC_CH_PARITY_MODE_NONE  /*based on manual P156*/
};


/*================================================[ public functions ]================================================*/

//--------------------------------------------------------------
/// \brief spi_hardware_init
/// Initialize spi hardware
/// \return void
//--------------------------------------------------------------

void spi_hardware_init(void) 
{
    
    XMC_GPIO_CONFIG_t stGPIOConfig;
    /*Initialize and Start SPI*/
    XMC_SPI_CH_Init(SPI_CHANNEL, &spi_config);

    XMC_SPI_CH_SetWordLength(SPI_CHANNEL, 16); // word is 16 bits
    XMC_SPI_CH_SetFrameLength(SPI_CHANNEL, 16);	//Frame is 16bits

    XMC_SPI_CH_SetBitOrderMsbFirst(SPI_CHANNEL);//High bit transmit first
    //XMC_SPI_CH_SetTransmitMode(SPI_CHANNEL, XMC_SPI_CH_MODE_STANDARD);//SPI standard full duplex mode
    XMC_SPI_CH_ConfigureShiftClockOutput(SPI_CHANNEL, XMC_USIC_CH_BRG_SHIFT_CLOCK_PASSIVE_LEVEL_0_DELAY_ENABLED, XMC_USIC_CH_BRG_SHIFT_CLOCK_OUTPUT_SCLK);//SCLK out and 1/2 sheft
    //XMC_SPI_CH_EnableDataTransmission(SPI_CHANNEL);
    /*Input source selected*/
    XMC_SPI_CH_SetInputSource(SPI_CHANNEL, XMC_SPI_CH_INPUT_DIN0, SPI_MISO_SOURCE);
    //XMC_SPI_CH_EnableEvent(SPI_CHANNEL, XMC_SPI_CH_EVENT_STANDARD_RECEIVE);
    XMC_SPI_CH_Start(SPI_CHANNEL);
    /*GPIO configuration*/

    stGPIOConfig.mode = XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT2;
    //stGPIOConfig.output_level = XMC_GPIO_OUTPUT_LEVEL_LOW;
    stGPIOConfig.output_strength = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE;

    XMC_GPIO_Init(SPI_MOSI, &stGPIOConfig);


    stGPIOConfig.mode = XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT2;
    stGPIOConfig.output_level = XMC_GPIO_OUTPUT_LEVEL_HIGH;
    stGPIOConfig.output_strength = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE;

    XMC_GPIO_Init(SPI_CS, &stGPIOConfig);


    stGPIOConfig.mode = XMC_GPIO_MODE_OUTPUT_PUSH_PULL_ALT2;
    //stGPIOConfig.output_level = XMC_GPIO_OUTPUT_LEVEL_LOW;
    stGPIOConfig.output_strength = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE;

    XMC_GPIO_Init(SPI_SCLK, &stGPIOConfig);

    stGPIOConfig.mode = XMC_GPIO_MODE_INPUT_TRISTATE;
    //stGPIOConfig.output_level = XMC_GPIO_OUTPUT_LEVEL_HIGH;
    stGPIOConfig.output_strength = XMC_GPIO_OUTPUT_STRENGTH_STRONG_SHARP_EDGE;

    XMC_GPIO_Init(SPI_MISO, &stGPIOConfig);
}
//--------------------------------------------------------------
/// \brief tlf_spi_transmit_recevie
/// this function work in full duplex mode
/// \return void
//--------------------------------------------------------------
s8 tlf_spi_transmit_recevie(u16 *data_in, const u16 *data_out,  u16 len)
{
    u32 current_tic = global_timer;
    s8 pd_ret = 1;
    u16 len_temp = len;
    /*if(data_in == 0 || len == 0 || data_out == 0)
    {
        return 0;
    }*/
    //mcu_set_output(mcu_output_TLF_SPI_CS, 0);
    XMC_SPI_CH_EnableSlaveSelect(SPI_CHANNEL, XMC_SPI_CH_SLAVE_SELECT_0);
    while(len_temp > 0)
    {
        SPI_CHANNEL->CCR = (SPI_CHANNEL->CCR & (u32)(~USIC_CH_CCR_HPCEN_Msk)) | (((u32) XMC_SPI_CH_MODE_STANDARD << USIC_CH_CCR_HPCEN_Pos) & (u32)USIC_CH_CCR_HPCEN_Msk);

        /* Check FIFO size */
        if ((SPI_CHANNEL->TBCTR & USIC_CH_TBCTR_SIZE_Msk) == 0U)
        {
            while((u32)XMC_USIC_CH_GetTransmitBufferStatus(SPI_CHANNEL) == (u32)XMC_USIC_CH_TBUF_STATUS_BUSY)
            {
                if((global_timer - current_tic) > tlf_spi_delay_cnt)
                {
                    pd_ret = 0;
                    break;
                }
            }

            XMC_SPI_CH_ClearStatusFlag(SPI_CHANNEL, (u32)XMC_SPI_CH_STATUS_FLAG_TRANSMIT_BUFFER_INDICATION);

            SPI_CHANNEL->TBUF[XMC_SPI_CH_MODE_STANDARD] = *data_out;
        }
        else
        {
            SPI_CHANNEL->IN[XMC_SPI_CH_MODE_STANDARD] = *data_out;
        }
        spi_delay(1);
        /* read back data */
        if ((SPI_CHANNEL->RBCTR & USIC_CH_RBCTR_SIZE_Msk) == 0U)
        {
            *data_in = (u16)SPI_CHANNEL->RBUF;
        }
        else
        {
            *data_in = (u16)SPI_CHANNEL->OUTR;
        }
        XMC_SPI_CH_ClearStatusFlag(SPI_CHANNEL, (u32)XMC_SPI_CH_STATUS_FLAG_RECEIVE_INDICATION);
        len_temp--;
        //data_in++;
        //data_out++;
    }

    //mcu_set_output(mcu_output_TLF_SPI_CS, 1);
    return pd_ret;
}

u16 write_spi_word(U16 spi_data)
{    
    u16 data;

    //XMC_GPIO_SetOutputLevel(P2_3, XMC_GPIO_OUTPUT_LEVEL_LOW);
    XMC_SPI_CH_EnableSlaveSelect(SPI_CHANNEL, XMC_SPI_CH_SLAVE_SELECT_0);
    XMC_SPI_CH_Transmit(SPI_CHANNEL, spi_data, XMC_SPI_CH_MODE_STANDARD);

    /*Wait till the byte has been transmitted*/
    while((XMC_SPI_CH_GetStatusFlag(SPI_CHANNEL) & XMC_SPI_CH_STATUS_FLAG_TRANSMIT_SHIFT_INDICATION) == 0U)
    {
        ;
    }
    XMC_SPI_CH_ClearStatusFlag(SPI_CHANNEL, XMC_SPI_CH_STATUS_FLAG_TRANSMIT_SHIFT_INDICATION);

    /*Wait till the byte has been transmitted*/
    //while((XMC_SPI_CH_GetStatusFlag(SPI_CHANNEL) & XMC_SPI_CH_STATUS_FLAG_RECEIVE_INDICATION) == 0U);
    //XMC_SPI_CH_ClearStatusFlag(SPI_CHANNEL, XMC_SPI_CH_STATUS_FLAG_RECEIVE_INDICATION);

    data = XMC_SPI_CH_GetReceivedData(SPI_CHANNEL);
    data = XMC_SPI_CH_GetReceivedData(SPI_CHANNEL);
    /*Disable Slave Select line */
    //XMC_SPI_CH_DisableSlaveSelect(SPI_CHANNEL);

    //XMC_GPIO_SetOutputLevel(P2_3, XMC_GPIO_OUTPUT_LEVEL_HIGH);
    
    return data;
}

